package service

import (
	"context"
	"fmt"
	"github.com/json-iterator/go"
	"time"

	"gitee.com/zhaokangming/tiny-ticktok/dao"
	"gitee.com/zhaokangming/tiny-ticktok/model"
	"gitee.com/zhaokangming/tiny-ticktok/pkg/app"
	"gitee.com/zhaokangming/tiny-ticktok/public/mock"
)

type FeedResponse struct {
	app.Response
	VideoList []model.Video `json:"video_list,omitempty"`
	NextTime  int64         `json:"next_time,omitempty"`
}

// CreateVideo create a single video
func CreateVideo(ctx context.Context, uid int64, video model.Video) error {
	return dao.CreateVideo(ctx, dao.Video{
		AuthorID: uid,
		PlayUrl:  video.PlayUrl,
		CoverUrl: video.CoverUrl,
		Title:    video.Title,
	})
}

// MGetVideos 返回视频列表
// 根据目前我们这个业务场景来讲取视频一次取30个，并且视频发布不是很频繁的情况下，
// 考虑在第一次查询的时候直接搞30分钟的cache,如果cache不存在的情况下，进行Mysql的读取
func MGetVideos(ctx context.Context, curID, nextTime int64) (*FeedResponse, error) {
	res := new(FeedResponse)
	res.NextTime = time.Now().Unix()

	// 从redis中先判断有无缓存
	cache := dao.RDB.Get(ctx, "videosCache").Val()
	if cache != "" {

		var json = jsoniter.ConfigCompatibleWithStandardLibrary
		err := json.Unmarshal([]byte(cache), &res.VideoList)
		if err != nil {
			// 但凡这边缓存走不通都应该走一边nocache的方式
			err := MGetVideosNoCache(ctx, curID, nextTime, res)
			if err != nil {
				// mysql还走不通直接返回
				return nil, err
			}
		}
		return res, nil
	}

	// 说明无缓存此时进行mysql的读取
	err := MGetVideosNoCache(ctx, curID, nextTime, res)
	if err != nil {
		return nil, err
	}
	return res, nil
}

// MGetVideosNoCache  没有缓存的情况：从Mysql数据库中获取全部的视频流信息 -> 存入redis缓存
func MGetVideosNoCache(ctx context.Context, curID, nextTime int64, res *FeedResponse) error {
	// 从redis中进行获取全部视频数据
	videos, err := dao.MGetVideosFromRedis(ctx)
	if err != nil {
		videos, err = dao.MGetVideos(ctx, nextTime)
		if err != nil {
			return err
		}
	}

	// 从mysql中填充videos的信息(即res.videoList)
	err = FillVideoList(ctx, curID, videos, nil, res)
	if err != nil {
		return err
	}

	// json化并存入redis
	err = dao.SetEXOfVideos(ctx, res.VideoList, "videosCache", 30*time.Minute)
	if err != nil {
		return err
	}

	return nil
}

// MGetVideosByUID 获取某一用户的所有视频列表,同样是判断个人投稿的缓存是否存在 -> 如果没缓存去mysql中寻找
func MGetVideosByUID(ctx context.Context, uid, targetID int64) (*FeedResponse, error) {
	res := new(FeedResponse)
	// 从redis中先判断有无缓存
	cache := dao.RDB.Get(ctx, fmt.Sprintf("videosCache:%d", targetID)).Val()
	if cache != "" {

		var json = jsoniter.ConfigCompatibleWithStandardLibrary
		err := json.Unmarshal([]byte(cache), &res.VideoList)
		if err != nil {
			// 但凡这边缓存走不通都应该走一边nocache的方式
			err := MGetVideosByUIDNoCache(ctx, uid, targetID, res)
			if err != nil {
				// mysql还走不通直接返回
				return nil, err
			}
		}
		return res, nil
	}

	// 没有cache的情况
	err := MGetVideosByUIDNoCache(ctx, uid, targetID, res)
	if err != nil {
		return nil, err
	}

	return res, nil
}

// MGetVideosByUIDNoCache  个人视频中没有缓存的情况:从mysql取出来，并进行缓存
func MGetVideosByUIDNoCache(ctx context.Context, uid, targetID int64, res *FeedResponse) error {
	// 获取某一用户的所有视频列表
	videos, err := dao.MGetVideosByUID(ctx, targetID)
	if err != nil {
		return err
	}

	user, err := QueryUser(ctx, uid, targetID)
	if err != nil {
		return err
	}

	// 填充视频信息
	err = FillVideoList(ctx, uid, videos, user, res)
	if err != nil {
		return err
	}

	// json化并存入redis,key值为：videosCacheOf+uid,过期时间为60min(因为个人投稿的频率应该更低）
	err = dao.SetEXOfVideos(ctx, res.VideoList, fmt.Sprintf("videosCache:%d", uid), -1)
	if err != nil {
		return err
	}

	return nil
}

// FillVideoList 对mysql数据库的查出来的video信息进行填充:Author,id,FavoriteCount,CommentCount
// 传入user的目的是判断调用的接口方是否是查询个人的，如果是个人直接填充
func FillVideoList(ctx context.Context, curID int64, videos []dao.Video, user *model.User, res *FeedResponse) error {

	res.VideoList = make([]model.Video, len(videos))
	for i, v := range videos {
		// 定义错误、以及视频作者变量
		var (
			err       error
			userVideo *model.User
		)

		// user不等于空时说明传进来的是查找个人的，直接赋值
		// 这样做的目的是在抽象的结果下个人的查询接口，只需调一次QueryUser（因为作者都相同）
		if user != nil {
			userVideo = user
		} else {
			// 从mysql查询作者信息，有error直接return
			userVideo, err = QueryUser(ctx, curID, v.AuthorID)
			if err != nil {
				return err
			}
		}

		// 填充列表
		res.VideoList[i].Author = *userVideo
		res.VideoList[i].Id = int64(v.ID)
		res.VideoList[i].Title = v.Title
		res.VideoList[i].FavoriteCount = dao.QueryFavoriteCount(ctx, int64(v.ID))
		res.VideoList[i].CommentCount, err = dao.QueryCommentCount(ctx, int64(v.ID))
		if err != nil {
			return err
		}

		res.VideoList[i].IsFavorite = dao.QueryIsFavorite(ctx, userVideo.Id, int64(v.ID))
		res.VideoList[i].CoverUrl = v.CoverUrl
		res.VideoList[i].PlayUrl = v.PlayUrl
	}

	// 如果视频列表为空进行mock
	if len(res.VideoList) == 0 {
		res.VideoList = append(res.VideoList, mock.DemoVideos...)
	}

	return nil
}
